package com.ruyuan.rapid.core.discovery;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ruyuan.rapid.common.config.*;
import com.ruyuan.rapid.common.constants.BasicConst;
import com.ruyuan.rapid.common.constants.RapidProtocol;
import com.ruyuan.rapid.common.util.FastJsonConvertUtil;
import com.ruyuan.rapid.common.util.Pair;
import com.ruyuan.rapid.common.util.ServiceLoader;
import com.ruyuan.rapid.core.RapidConfig;
import com.ruyuan.rapid.discovery.api.Notify;
import com.ruyuan.rapid.discovery.api.Registry;
import com.ruyuan.rapid.discovery.api.RegistryService;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.concurrent.CountDownLatch;

/**
 * 网管服务的注册中心管理类
 * @author mac
 */
@Slf4j
public class RegistryManager {

    private RegistryService registryService;

    private static RapidConfig rapidConfig;

    private static String superPath;

    private static String servicesPath;

    private static String instancesPath;

    private static String rulesPath;

    private static String gatewayPath;

    private CountDownLatch countDownLatch = new CountDownLatch(1);

    private RegistryManager() {
    }

    private static class SingletonHolder {
        private static final RegistryManager INSTANCE = new RegistryManager();
    }

    public static RegistryManager getInstance() {
        return SingletonHolder.INSTANCE;
    }

    public void initialized(RapidConfig rapidConfig) throws Exception {
        RegistryManager.rapidConfig = rapidConfig;
        superPath = Registry.PATH + rapidConfig.getNamespace() + BasicConst.BAR_SEPARATOR + rapidConfig.getEnv();
        servicesPath = superPath + Registry.SERVICE_PREFIX;
        instancesPath = superPath + Registry.INSTANCE_PREFIX;
        rulesPath = superPath + Registry.RULE_PREFIX;
        gatewayPath = superPath + Registry.GATEWAY_PREFIX;

        //	2. 初始化加载注册中心对象
        ServiceLoader<RegistryService> serviceLoader = ServiceLoader.load(RegistryService.class);
        for (RegistryService registryService : serviceLoader) {
            registryService.initialized(rapidConfig.getRegistryAddress());
            this.registryService = registryService;
        }

        // 3. 注册监听
        registryService.addWatcherListeners(superPath, new ServiceListener());

        // 4. 订阅服务
        subscribeService();

        // 5. 注册自身服务
        RegistryServer registryServer = new RegistryServer(registryService);
        registryServer.registerSelf();
    }

    /**
     * 订阅服务: 拉取etcd注册中心的所有需要使用的元数据信息, 解析并放置到缓存中
     */
    /*
     /RuyuanRapid-dev
          /servicecs
               /hello:1.0.0
               /say:1.0.0
     */
    private synchronized void subscribeService() {
        log.info("RegistryManager subscribeService --------------- 服务订阅开始 -------------");

        try {
            // 加载服务定义和服务实例的集合, 获取servicePath=/RuyuanRapid-dev/services 下面所有的列表
            List<Pair<String, String>> definitionList = registryService.getListByPrefixKey(servicesPath);
            for (Pair<String, String> definition : definitionList) {
                String definitionPath = definition.getObject1();
                String definitionJson = definition.getObject2();

                // 把当前获取的根目录进行排除
                if (definitionPath.equals(servicesPath)) {
                    continue;
                }
                String uniqueId = definitionPath.substring(servicesPath.length() + 1);

                ServiceDefinition serviceDefinition = parseServiceDefinition(definitionJson);
                DynamicConfigManager.getInstance().putServiceDefinition(uniqueId, serviceDefinition);
                log.info("RegistryManager subscribeService 加载服务定义, uniqueId: {}, serviceDefinition: {}", uniqueId,
                        FastJsonConvertUtil.convertObjectToJSON(serviceDefinition));

                // 加载服务实例集合
                String serviceInstancePrefix = instancesPath + Registry.PATH + uniqueId;
                List<Pair<String, String>> instanceList = registryService.getListByPrefixKey(serviceInstancePrefix);

                Set<ServiceInstance> serviceInstanceSet = new HashSet<>();
                for (Pair<String, String> instance : instanceList) {
                    String instancePath = instance.getObject1();
                    String instanceJson = instance.getObject2();

                    ServiceInstance serviceInstance = FastJsonConvertUtil.convertJSONToObject(instanceJson, ServiceInstance.class);
                    serviceInstanceSet.add(serviceInstance);
                }

                DynamicConfigManager.getInstance().addServiceInstance(uniqueId, serviceInstanceSet);
                log.info("RegistryManager subscribeService 加载服务实例, uniqueId: {}, serviceInstance: {}", uniqueId,
                        FastJsonConvertUtil.convertObjectToJSON(serviceInstanceSet));

                // 加载规则集合
                List<Pair<String, String>> ruleList = registryService.getListByPrefixKey(rulesPath);
                for (Pair<String, String> r : ruleList) {
                    String rulePath = r.getObject1();
                    String ruleJson = r.getObject2();
                    if (rulePath.endsWith(rulesPath)) {
                        continue;
                    }
                    Rule rule = FastJsonConvertUtil.convertJSONToObject(ruleJson, Rule.class);
                    DynamicConfigManager.getInstance().putRule(rule.getId(), rule);
                    log.info("RegistryManager subscribeService 加载规则信息, ruleId: {}, rule: {}", rule.getId(),
                            FastJsonConvertUtil.convertObjectToJSON(rule));
                }
            }
        } catch (Exception e) {
            log.error("RegistryManager subscribeService 服务订阅失败", e);
        } finally {
            countDownLatch.countDown();
            log.info("RegistryManager subscribeService --------------- 服务订阅结束 -------------");
        }
    }

    /**
     * 解析服务定义信息json, 返回服务定义对象
     * @param definitionJson
     * @return
     */
    private ServiceDefinition parseServiceDefinition(String definitionJson) {
        Map<String, Object> jsonMap = FastJsonConvertUtil.convertJSONToObject(definitionJson, Map.class);
        ServiceDefinition serviceDefinition = new ServiceDefinition();

        // 填充ServiceDefinition
        serviceDefinition.setUniqueId((String) jsonMap.get("uniqueId"));
        serviceDefinition.setServiceId((String) jsonMap.get("serviceId"));
        serviceDefinition.setProtocol((String) jsonMap.get("protocol"));
        serviceDefinition.setPatternPath((String) jsonMap.get("patternPath"));
        serviceDefinition.setVersion((String) jsonMap.get("version"));
        serviceDefinition.setEnable((Boolean) jsonMap.get("enable"));
        serviceDefinition.setEnvType((String) jsonMap.get("envType"));
        JSONObject jsonInvokerMap = (JSONObject) jsonMap.get("invokerMap");
        Map<String, ServiceInvoker> invokerMap = new HashMap<>();
        switch (serviceDefinition.getProtocol()) {
            case RapidProtocol.HTTP:
                Map<String, Object> httpInvokerMap = FastJsonConvertUtil.convertJSONToObject(jsonInvokerMap, Map.class);
                for (Map.Entry<String, Object> me : httpInvokerMap.entrySet()) {
                    String path = me.getKey();
                    JSONObject jsonInvoker = (JSONObject) me.getValue();
                    HttpServiceInvoker httpServiceInvoker = FastJsonConvertUtil.convertJSONToObject(jsonInvoker, HttpServiceInvoker.class);
                    invokerMap.put(path, httpServiceInvoker);
                }
                break;
            case RapidProtocol.DUBBO:
                Map<String, Object> dubboInvokerMap = FastJsonConvertUtil.convertJSONToObject(jsonInvokerMap, Map.class);
                for (Map.Entry<String, Object> me : dubboInvokerMap.entrySet()) {
                    String path = me.getKey();
                    JSONObject jsonInvoker = (JSONObject) me.getValue();
                    DubboServiceInvoker dubboServiceInvoker = FastJsonConvertUtil.convertJSONToObject(jsonInvoker, DubboServiceInvoker.class);
                    invokerMap.put(path, dubboServiceInvoker);
                }
                break;
            default:
                break;
        }
        serviceDefinition.setInvokerMap(invokerMap);
        return serviceDefinition;
    }

    class ServiceListener implements Notify {
        /**
         * 添加
         * @param key
         * @param value
         * @throws Exception
         */
        @Override
        public void put(String key, String value) throws Exception {
            countDownLatch.await();
            if (servicesPath.equals(key) || instancesPath.equals(key) || rulesPath.equals(key)) {
                return;
            }

            // 如果是服务定义发生变更
            if (key.contains(servicesPath)) {
                String uniqueId = key.substring(servicesPath.length() + 1);
                ServiceDefinition serviceDefinition = parseServiceDefinition(value);
                DynamicConfigManager.getInstance().putServiceDefinition(uniqueId, serviceDefinition);
                return;
            }

            // 如果是服务实例发生变更
            if (key.contains(instancesPath)) {
                String temp = key.substring(instancesPath.length() + 1);
                String[] tempArray = temp.split(Registry.PATH);
                if (tempArray.length == 2) {
                    String uniqueId = tempArray[0];
                    ServiceInstance serviceInstance = FastJsonConvertUtil.convertJSONToObject(value, ServiceInstance.class);
                    DynamicConfigManager.getInstance().updateServiceInstance(uniqueId, serviceInstance);
                }
                return;
            }

            // 如果是规则发生变更
            if (key.contains(rulesPath)) {
                String ruleId = key.substring(rulesPath.length() + 1);
                Rule rule = FastJsonConvertUtil.convertJSONToObject(value, Rule.class);
                DynamicConfigManager.getInstance().putRule(ruleId, rule);
                return;
            }
        }

        /**
         * 删除
         * @param key
         * @throws Exception
         */
        @Override
        public void delete(String key) throws Exception {
            countDownLatch.await();
            if (servicesPath.equals(key) || instancesPath.equals(key) || rulesPath.equals(key)) {
                return;
            }

            // 如果是服务定义发生变更
            if (key.contains(servicesPath)) {
                String uniqueId = key.substring(servicesPath.length() + 1);
                DynamicConfigManager.getInstance().removeServiceDefinition(uniqueId);
                DynamicConfigManager.getInstance().removeServiceInstancesByUniqueId(uniqueId);
                return;
            }

            // 如果是服务实例发生变更
            if (key.contains(instancesPath)) {
                String temp = key.substring(instancesPath.length() + 1);
                String[] tempArray = temp.split(Registry.PATH);
                if (tempArray.length == 2) {
                    String uniqueId = tempArray[0];
                    String instanceId = tempArray[1];
                    DynamicConfigManager.getInstance().removeServiceInstance(uniqueId, instanceId);
                }
                return;
            }

            // 如果是规则发生变更
            if (key.contains(rulesPath)) {
                String ruleId = key.substring(rulesPath.length() + 1);
                DynamicConfigManager.getInstance().removeRule(ruleId);
                return;
            }
        }
    }

    static class RegistryServer {

        private RegistryService registryService;

        private String selfPath;

        public RegistryServer(RegistryService registryService) throws Exception {
            this.registryService = registryService;
            // 注册父级目录
            this.registryService.registerPathIfNotExists(superPath, "", true);
            // 注册网关目录
            this.registryService.registerPathIfNotExists(gatewayPath, "", true);
            this.selfPath = gatewayPath + Registry.PATH + rapidConfig.getRapidId();
        }

        public void registerSelf() throws Exception {
            String rapidConfigJson = FastJsonConvertUtil.convertObjectToJSON(rapidConfig);
            this.registryService.registerPathIfNotExists(selfPath, rapidConfigJson, false);
        }
    }

    public RegistryService getRegistryService() {
        return registryService;
    }

    public RapidConfig getRapidConfig() {
        return rapidConfig;
    }

    public static String getSuperPath() {
        return superPath;
    }

    public static String getServicesPath() {
        return servicesPath;
    }

    public static String getInstancesPath() {
        return instancesPath;
    }

    public static String getRulesPath() {
        return rulesPath;
    }

    public static String getGatewayPath() {
        return gatewayPath;
    }

    public static void main(String[] args) {
        JSONObject jsonInvoker = new JSONObject();
        jsonInvoker.put("invokerPath", "testEntity");
        jsonInvoker.put("ruleId", "1");
        jsonInvoker.put("timeout", 2000);
        HttpServiceInvoker httpServiceInvoker = FastJsonConvertUtil.convertJSONToObject(jsonInvoker, HttpServiceInvoker.class);
        System.out.println(JSON.toJSONString(httpServiceInvoker));
    }
}
